package com.xiaoxin.common.doc.config;


import com.github.xiaoymin.knife4j.spring.extension.OpenApiExtensionResolver;
import com.xiaoxin.common.doc.config.properties.SpringDocProperties;
import io.swagger.annotations.ApiOperation;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.info.License;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.util.ObjectUtils;
import springfox.bean.validators.configuration.BeanValidatorPluginsConfiguration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.oas.annotations.EnableOpenApi;
import springfox.documentation.service.*;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spi.service.contexts.SecurityContext;
import springfox.documentation.spring.web.plugins.Docket;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * swagger 配置属性
 *
 * @author ZhangXX
 */
@EnableOpenApi
@AutoConfiguration
@EnableConfigurationProperties(SpringDocProperties.class)
@ConditionalOnProperty(name = "knife4j.enabled", havingValue = "true", matchIfMissing = true)
@Import(BeanValidatorPluginsConfiguration.class)
public class Knife4jAutoConfig {

    private final OpenApiExtensionResolver openApiExtensionResolver;

    public Knife4jAutoConfig(OpenApiExtensionResolver openApiExtensionResolver) {
        this.openApiExtensionResolver = openApiExtensionResolver;
    }

    @Bean(value = "defaultApi1")
    public Docket defaultApi(SpringDocProperties properties) {
        // 文档基本信息
        SpringDocProperties.InfoProperties infoProperties = properties.getInfo();
        ApiInfo info = convertInfo(infoProperties);
        Docket docket = new Docket(DocumentationType.OAS_30)
                .apiInfo(info)
                //分组名称
                .groupName(infoProperties.getGroupName())
                .select()
                // 扫描所有有注解的api，用这种方式更灵活
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                .paths(PathSelectors.any())
                .build();
        Components components = properties.getComponents();
        if (!ObjectUtils.isEmpty(components)) {
            Map<String, io.swagger.v3.oas.models.security.SecurityScheme> securitySchemeList = components.getSecuritySchemes();
            ArrayList<SecurityContext> securityContexts = new ArrayList<>();
            ArrayList<SecurityScheme> securitySchemes = new ArrayList<>();
            securitySchemeList.forEach((k, v) -> {
                securitySchemes.add(apiKey(k, v.getName(), v.getIn().toString()));
                securityContexts.add(securityContext(k));
            });
            docket.securityContexts(securityContexts).securitySchemes(securitySchemes);
        }
        return docket;
    }

    private ApiInfo convertInfo(SpringDocProperties.InfoProperties infoProperties) {
        License license = infoProperties.getLicense();
        io.swagger.v3.oas.models.info.Contact contact = infoProperties.getContact();
        if (ObjectUtils.isEmpty(contact)) {
            contact = new io.swagger.v3.oas.models.info.Contact();
        }
        boolean licenseEmpty = ObjectUtils.isEmpty(license);
        return new ApiInfoBuilder()
                .title(infoProperties.getTitle())
                .description(infoProperties.getDescription())
                .termsOfServiceUrl(infoProperties.getTermsOfServiceUrl())
                .contact(new Contact(contact.getName(), contact.getUrl(), contact.getEmail()))
                .license(licenseEmpty ? null : license.getName())
                .licenseUrl(licenseEmpty ? null : license.getUrl())
                .version(infoProperties.getVersion())
                .build();
    }

    private List<SecurityScheme> securitySchemes() {
        List<SecurityScheme> apiKeyList = new ArrayList<>();
        apiKeyList.add(HttpAuthenticationScheme.JWT_BEARER_BUILDER
                .name("Authorization")
                .description("Bearer Token")
                .build());
        return apiKeyList;
    }

    private ApiKey apiKey(String name, String keyName, String passAs) {
        return new ApiKey(name, keyName, passAs);
    }

    private SecurityContext securityContext(String securityReferenceName) {
        return SecurityContext.builder().securityReferences(defaultAuth(securityReferenceName))
                .forPaths(PathSelectors.regex("/.*")).build();
    }

    List<SecurityReference> defaultAuth(String securityReferenceName) {
        AuthorizationScope authorizationScope = new AuthorizationScope("global", "accessEverything");
        AuthorizationScope[] authorizationScopes = new AuthorizationScope[1];
        authorizationScopes[0] = authorizationScope;
        return new ArrayList<SecurityReference>() {{
            add(new SecurityReference(securityReferenceName, authorizationScopes));
        }};
    }

}
